import path = require("path");

const xmlreader = require("xmlreader");
const fs = require("fs");
const ejs = require('ejs');
const iconv = require('iconv-lite');
//import vscode = require('vscode');

function base64Encode(str: string) {
    const buf = Buffer.from(str, 'utf8');
    return buf.toString('base64');
}
function base64Decode(base64str: string) {
    const buf = Buffer.from(base64str, 'base64');
    return JSON.parse(buf.toString('utf8'));
}

//const templateDir = 'C:\\Users\\Doraemon\\Desktop\\draw\\vscode-drawio\\template\\application';

const BOM = "\ufeff";
export async function generateCode(dioXmlString: string, targetProjectDir: any, extensionPath: string) {
    let applicationMap = new Map();
    let portMap = new Map();
    let interfaceMap = new Map();
    let componentMap = new Map();
    let edges: any = [];
    var appList: any = [];

    function getAppModel() {
        applicationMap.forEach((appObj, appId) => {
            var currentApp = {
                name: appObj.value,
                components: []
            };

            componentMap.forEach((component, componentId) => {
                if (component.parent !== appId) {//该组件不归属于该容器
                    return;
                }
                var currentComponent = {
                    componentId: componentId,
                    componentName: component.cComponentType,
                    instanceName: component.value,
                    interfaces: [],
                    ports: [],
                    hasUi: component.cHasUi === '1'
                };
                //过滤接口
                interfaceMap.forEach((theInterface: any, interfaceId: any) => {
                    if (theInterface.parent !== componentId) {//该接口不归属于该组件
                        return;
                    }
                    var currentInterface: any = {
                        interfaceId: interfaceId,
                        interfaceName: theInterface.cInterfaceName,
                        interfaceModifier: theInterface.cInterfaceModifier,
                        interfaceType: theInterface.cInterfaceType,
                        serviceName: theInterface.cServiceName,//[currentApp.name, currentComponent.componentName, interfaceObj.serviceTypeName, interfaceId].join('-')//统一定位边的起点的id为接口的id
                    };
                    /*
                    let others = edges.filter((edge: any) => {//如果该接口是某一条边的终点
                        return edge.target === interfaceId;//表明该接口与其他接口有连线关系
                    });
                    if (others.length === 1) {//那么把id改成起点
                        currentInterface.serviceName = [currentApp.name, currentComponent.componentName, interfaceObj.serviceType, others[0].source].join('-')
                    }*/
                    currentComponent.interfaces.push(currentInterface as never);
                });
                //过滤端口
                portMap.forEach((thePort, portId) => {
                    if (thePort.parent !== componentId) {
                        return;
                    }
                    let currentPort = {
                        portId: portId,
                        portName: thePort.cPortName,
                        portModifier: thePort.cPortModifier,
                        portType: thePort.cPortType
                    };
                    currentComponent.ports.push(currentPort as never);
                });
                //把组件添加到应用里
                currentApp.components.push(currentComponent as never);
            });
            //把应用添加到应用列表里
            appList.push(currentApp);
        });
        //最终返回applist
        return appList;
    };

    function readXml(dioXmlString: string) {
        xmlreader.read(dioXmlString, function (errors: any, response: any) {
            if (errors) {
                console.log(errors)
                return;
            }
            if (!response.mxfile || !response.mxfile.diagram) {
                console.log('xml文件格式错误');
                return;
            }
            response.mxfile.diagram.mxGraphModel.root.mxCell.each((i: number, cell: any) => {
                let attrs = cell.attributes();
                if (attrs.cType == 'application') {
                    applicationMap.set(attrs.id, attrs);
                } else if (attrs.cType == 'component') {
                    componentMap.set(attrs.id, attrs)
                } else if (attrs.cType == 'port') {
                    portMap.set(attrs.id, attrs)
                } else if (attrs.cType == 'interface') {
                    interfaceMap.set(attrs.id, attrs)
                } else if (attrs.edge) {
                    edges.push({
                        source: attrs.source,
                        target: attrs.target
                    });
                }
            });
        });
    }

    /**
 * 递归克隆模板文件，并根据appList实例化模板
 * @param srcPath 模板文件所在文件夹
 * @param projectDir 将要生成的项目的保存路径
 * @param currentTargetSuffix 递归时保存当前递归的路径，初始时应该为空字符串
 */
    function createFileFromTemplateRecursively(srcPath: string, projectDir: string, currentTargetSuffix: string) {
        fs.readdir(srcPath, function (err: Error, files: any) {
            if (err) {
                console.warn(err)
            } else {
                //遍历读取到的文件列表
                files.forEach(function (filename: any) {
                    //替换文件名
                    //获取当前文件的绝对路径
                    var srcAbsoluteDir = path.join(srcPath, filename);//模板文件的绝对路径
                    var newTargetPathSuffix = path.join(currentTargetSuffix, filename);//目标文件的路径前缀（部分路径），目标文件最终路径为：前缀+子项目名+模板文件
                    //根据文件路径获取文件信息，返回一个fs.Stats对象
                    fs.stat(srcAbsoluteDir, function (eror: any, stats: any) {
                        if (eror) {
                            console.warn('获取文件stats失败');
                        } else {
                            if (stats.isFile()) {//是文件
                                appList.forEach((app: any) => {
                                    ejs.renderFile(srcAbsoluteDir, { app }).then((res: any) => {//渲染模板
                                        //console.log(res);
                                        let currentProjectFile = path.join(projectDir, app.name, currentTargetSuffix, filename.replace('template', app.name));//拼接子项目的路径
                                        let currentProjectDir = path.dirname(currentProjectFile);
                                        fs.access(currentProjectDir, fs.constants.F_OK, (err: any) => {
                                            if (err) {//目录不存在，先建立目录
                                                fs.mkdirSync(currentProjectDir, { recursive: true });
                                            }
                                            fs.writeFile(currentProjectFile, BOM + res, (err: any) => {//保存渲染后的结果到文件
                                                if (err) {
                                                    console.log('文件写入错误');
                                                } else {
                                                    console.log('文件已经建好了');
                                                }
                                            })
                                        })

                                    });
                                })
                            }
                            else if (stats.isDirectory()) {//是文件夹
                                createFileFromTemplateRecursively(srcAbsoluteDir, projectDir, newTargetPathSuffix);//递归，如果是文件夹，就继续遍历该文件夹下面的文件
                            }
                        }
                    })
                })
            }
        });
    }
    readXml(dioXmlString);
    getAppModel();
    console.log(JSON.stringify(appList));
    const templateDir = path.join(extensionPath, 'template', 'application');
    createFileFromTemplateRecursively(templateDir, targetProjectDir, '');
    //创建总的CMakeLists.txt
    const makefile = path.join(extensionPath, 'template', 'application-other', 'CMakeLists.txt');
    ejs.renderFile(makefile, { apps: appList }).then((res: any) => {//渲染模板
        fs.writeFile(path.join(targetProjectDir, path.basename(makefile)), res, (err: any) => {//保存渲染后的结果到文件
            if (err) {
                console.log('文件写入错误');
            } else {
                console.log('CMAKE文件已经建好了');
            }
        });
    });
}